#include "napi/native_api.h"

#include <hilog/log.h>
#include <thread>
#include <unistd.h>

#include "threadsafe_manager.h"

#define LOG_DOMAIN 0x0202
#define LOG_TAG "Test_TAG"

namespace {
constexpr size_t EVENT_TYPE_SIZE = 32;
constexpr size_t KEY_BUFFER_SIZE = 32;
constexpr size_t VALUE_BUFFER_SIZE = 1024 * 1024;
} // namespace
using namespace threadsafe;

#include <hitrace/trace.h>

struct ThreadLockInfo {
    std::mutex mutex;
    std::condition_variable condition;
    bool ready = false;
};

// 定义回传到JS线程的数据结构
struct CallBackData {
    string arg1;
    string arg2;
    string id;
    ThreadLockInfo *lockInfo;
};
/*
 * User-defined callback, it can be used for processing before the call js.
 * typedef void (*napi_threadsafe_function_call_js)(napi_env env,
                                                    napi_value js_callback,
                                                    void* context,
                                                    void* data);
 * */
static void CallJsFunction(napi_env env, napi_value cb, void *hint, void *data) {
//    OH_HiTrace_StartAsyncTrace("CallJsFunction", 3);
    std::thread::id this_id = std::this_thread::get_id();
//    OH_LOG_DEBUG(LOG_APP, "thread CallJs %{public}d.", this_id);
    TSInfo *theHint = reinterpret_cast<TSInfo *>(hint);

    // 调用JS回调
    napi_valuetype valueType = napi_undefined;
    napi_typeof(env, cb, &valueType);
//    OH_LOG_DEBUG(LOG_APP, "CallJs js_cb is napi_function: %{public}d", valueType == napi_function);
    if (env != nullptr && data != nullptr && theHint != nullptr && (valueType == napi_function)) {
        CallBackData *callData = reinterpret_cast<CallBackData *>(data);
        OH_HiTrace_FinishAsyncTrace(("schedule_task_" + callData->id).c_str(), 2);
        OH_HiTrace_StartAsyncTrace(("napi_call_function_" + callData->id).c_str(), 3);
        theHint->finalizeData = callData;
        napi_value argv[2] = {0};
        
        //-------------
        napi_status status;
        status = napi_create_string_utf8(env, callData->arg1.c_str(), NAPI_AUTO_LENGTH, &argv[0]);
        if (status) {
            OH_LOG_ERROR(LOG_APP, "napi_create_string_utf8 arg1 failed!");
            return;
        }

        status = napi_create_string_utf8(env, callData->arg2.c_str(), NAPI_AUTO_LENGTH, &argv[1]);
        if (status) {
            OH_LOG_ERROR(LOG_APP, "napi_create_string_utf8 arg2 failed!");
            return;
        }
        
        napi_value undefined;
        status = napi_get_undefined(env, &undefined);

        napi_value ret;
        
        status = napi_call_function(env, undefined, cb, 2, argv, &ret);
//        OH_LOG_DEBUG(LOG_APP, "CallJs napi_call_function: %{public}d", status == napi_ok);
        char* buf = new char[VALUE_BUFFER_SIZE];
        size_t length = 0;
        napi_get_value_string_utf8(env, ret, buf, VALUE_BUFFER_SIZE, &length);
        
//        int32_t *result = new int32_t;
//        napi_get_value_int32(env, ret, result);
        OH_HiTrace_FinishAsyncTrace(("napi_call_function_" + callData->id).c_str(), 3);
        OH_HiTrace_StartAsyncTrace(("notify_" + callData->id).c_str(), 4);
        ThreadSafeManager &tmr = ThreadSafeManager::GetThreadSafeManager();
        ResultInfo *resultInfo = new ResultInfo();
        resultInfo->data = reinterpret_cast<void *>(buf);
        tmr.SetResultInfo(callData->id, resultInfo);
        
        std::unique_lock<std::mutex> lck(callData->lockInfo->mutex);
//        OH_LOG_DEBUG(LOG_APP, "begin notify");
        callData->lockInfo->ready = true;
        callData->lockInfo->condition.notify_all();
//        OH_LOG_DEBUG(LOG_APP, "after notify");
    }
//    OH_HiTrace_FinishAsyncTrace("CallJsFunction", 3);
}

/*
 * User-defined callback, which is used to clear resources.
 * typedef void (*napi_finalize)(napi_env env,
                                 void* finalize_data,
                                 void* finalize_hint);
 * */
static void FinalizeCallBack(napi_env env, void *data, void *hint) {
//    OH_LOG_DEBUG(LOG_APP, "ThreadSafeTest FinalizeCallBack");
    CallBackData *cd = reinterpret_cast<CallBackData *>(data);
    if (cd != nullptr) {
//        OH_LOG_DEBUG(LOG_APP, "ThreadSafeTest delete cd");
        delete cd;
        cd = nullptr;
    }
    TSInfo *info = reinterpret_cast<TSInfo *>(hint);
    if (info != nullptr) {
//        OH_LOG_DEBUG(LOG_APP, "ThreadSafeTest delete info");
        delete info;
        info = nullptr;
    }
}

// 在JS线程上运行
static napi_value CreateThreadSafeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 2;
    napi_status status;
    napi_value argv[2] = {nullptr};
    status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    if (argc != 2) {
        OH_LOG_ERROR(LOG_APP, "Invalid params number: %{public}zu", argc);
        // "0000"：error code，一般由开发者自定义
        napi_throw_error(env, "0000", "Invalid params number");
    }
//    OH_LOG_DEBUG(LOG_APP, "ThreadSafeTest napi_get_cb_info: %{public}d", status == napi_ok);

    char buf[KEY_BUFFER_SIZE] = {0};
    size_t length = 0;
    napi_get_value_string_utf8(env, argv[0], buf, KEY_BUFFER_SIZE, &length);

    string funcName(buf, length);
    TSInfo *tsInfo = new TSInfo(funcName, CallJsFunction, FinalizeCallBack);

    ThreadSafeManager &tmr = ThreadSafeManager::GetThreadSafeManager();
    tmr.CreateThreadSafeFunction(env, argv[1], tsInfo);
    // 返回undefined
    return nullptr;
}


// Native线程任务demo:借助ThreadSafe能力，可以调用主线程的能力
static napi_value CallThreadSafeFunction(napi_env env, napi_callback_info info) {
    size_t argc = 1;
    napi_status status;
    napi_value argv[1] = {nullptr};
    status = napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
    char buf[KEY_BUFFER_SIZE] = {0};
    size_t length = 0;
    napi_get_value_string_utf8(env, argv[0], buf, KEY_BUFFER_SIZE, &length);
    string funcName(buf, length);
    std::thread t([funcName]() {
        static int num = 0;
        CallBackData *data = new CallBackData();
        data->arg1 =
            "This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite readable and maintains itself as a single string.This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite readable and maintains itself as a single string.This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite."
        ; // 512个char
        data->arg2 =
            "This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite readable and maintains itself as a single string.This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite readable and maintains itself as a single string.This is a very long string that spans multiple lines, but thanks to the string literal concatenation feature of C++, it is actually quite."
            ; // 512个char
        data->id = (funcName + std::to_string(num));
        OH_HiTrace_StartAsyncTrace(funcName.c_str(), 1);
        OH_HiTrace_StartAsyncTrace(("schedule_task_" + data->id).c_str(), 2);
        ThreadSafeManager &tmr = ThreadSafeManager::GetThreadSafeManager();
        std::shared_ptr<struct ThreadLockInfo> lockInfo = std::make_shared<struct ThreadLockInfo>();
        data->lockInfo = lockInfo.get();
        bool status = tmr.Call(funcName, reinterpret_cast<void *>(data), napi_tsfn_blocking);
        if (!status) {
            OH_LOG_ERROR(LOG_APP, "napi_call_threadsafe_function success!");
        }
        // 等待数据回传，使用wait notify等机制实现
        std::unique_lock<std::mutex> lck(lockInfo->mutex);
//        OH_LOG_DEBUG(LOG_APP, "begin wait!");
        lockInfo->condition.wait(lck, [&lockInfo] { return lockInfo->ready; });
        
        //        OH_LOG_DEBUG(LOG_APP, "after wait!");
        void *resultData = nullptr;
        ResultInfo *resultInfo = nullptr;
//        OH_LOG_INFO(LOG_APP, "=====CallThreadSafeFunction result=====");
        resultInfo = tmr.GetResultInfo(data->id);
        if (resultInfo != nullptr) {
            resultData = resultInfo->data;
            if (resultData == nullptr) {
                OH_LOG_ERROR(LOG_APP, "Invalid params number");
            }
        }
        OH_HiTrace_FinishAsyncTrace(("notify_" + data->id).c_str(), 4);
        OH_HiTrace_FinishAsyncTrace(funcName.c_str(), 1);

        char* result = reinterpret_cast<char *>(resultData);
//        OH_LOG_INFO(LOG_APP, "CallThreadSafeFunction result: %{public}s", result);
        if (resultData != nullptr) {
            delete resultData;
            resultData = nullptr;
        }
        if (resultInfo != nullptr) {
            delete resultInfo;
            resultInfo = nullptr;
        }
        if (data != nullptr) {
            delete data;
            data = nullptr;
        }
    });
    t.detach();
    return nullptr;
}

// 模块注册及初始化
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"createThreadSafeFunction", nullptr, CreateThreadSafeFunction, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"callThreadSafeFunction", nullptr, CallThreadSafeFunction, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = nullptr,
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }